package tp.pr5;

import tp.pr5.gui.*;
import tp.pr5.instructions.Instruction;
import tp.pr5.instructions.exceptions.InstructionExecutionException;
import tp.pr5.items.InventoryObserver;
import tp.pr5.items.ItemContainer;
/**
 * 
 * @author Carlos Bermúdez y Mario de Benito
 *
 */
public class RobotEngine extends Observable<RobotEngineObserver>{	
	//--
	private NavigationModule navCon;
	private boolean quit;
	//--
	//--
	private int fuel;
	private int recycledMaterial;
	private ItemContainer items;
	private Instruction lastInstruction;
	//--
	//private NavigationPanel navPan;
	//private RobotPanel rbtPan;
	
	/**
	 * Creates the robot engine in an initial place, facing at an initial direction and with a city map
	 * @param initialPlace
	 * @param direction
	 * @param cityMap
	 */
	public RobotEngine(City cityMap, Place initialPlace, Direction dir){
		//this.cityMap = cityMap.cityMap;		
		this.items = new ItemContainer();		
		this.navCon = new NavigationModule(cityMap, initialPlace);
		this.navCon.initHeading(dir);
		requestStart();
	}	
	/**
	 * Starts a new game
	 */
	public void requestStart(){
		this.quit = false;
		this.fuel = 100;
		this.recycledMaterial = 0;
		this.emitRobotUpdate();
		this.emitArrivedAtPlace();
		
	}
	
	/**
	 * Sets the the swîngInterface
	 */
	public void setSwingInterface(boolean b){
	}
	
	/**
	 * Sets the navigation panel for the robot
	 */
	/*public void setNavigationPanel(NavigationPanel navPan){
		this.navPan = navPan;
	}*/	
	/**
	 * Sets the robot panel for the robot
	 */
	/*public void setRobotPanel(RobotPanel rbtPan){
		this.rbtPan = rbtPan;
	}*/
	/**
	 * Sets the main window for the robot
	 */
	public void setGUIWindow(MainWindow main){
	}
	
	/**
	 * 	Adds an amount of fuel to the robot (it can be negative)
	 * @param fuel
	 */
	public void addFuel(int fuel) {
		this.fuel = this.fuel + fuel;
		emitRobotUpdate();
	}
	/**
	 * Increases the amount of recycled material of the robot 
	 * @param weight
	 */
	public void addRecycledMaterial(int weight){
		this.recycledMaterial = this.recycledMaterial + weight;
		emitRobotUpdate();
	} 
	/**
	 * 	Returns the current fuel level of the robot.
	 * @return
	 */
	public int getFuel(){
		return this.fuel;
	} 
	/**
	 * Returns the current weight of recycled material that the robot carries. This method is mandatory FOR TESTING PURPOSES
	 * @return
	 */
	public int getRecycledMaterial(){
		return this.recycledMaterial;
	} 
	/**
	 * Returns the street opposite the robot
	 * @return
	 */
	
	/**
	 * Returns fuel & recycled material status string
	 * @return
	 */
	public String getStatus(){
		return "      * My power is "+this.getFuel()+Main.LINE_SEPARATOR+
			   "      * My reclycled material is "+this.getRecycledMaterial();
	}
	/**
	 * Returns the items array
	 * @return
	 */
	public ItemContainer getInventory(){
		return this.items;
	}
	/**
	 * sets a new value for the fuel
	 * @param fuel
	 */
	public void setFuel(int fuel){
		this.fuel = fuel;
		emitRobotUpdate();
	}
	/**
	 * flags the robot to quit the game 
	 */
	public void requestQuit(){
		this.quit = true;
		//FINAL DEL JUEGO		
		emitQuit();	
	}
	/**
	 * prints the interpreter help
	 */
	public void requestHelp(){		
		for(RobotEngineObserver reo: this.lista){
			reo.communicationHelp(Interpreter.interpreterHelp());
		}
	}
	/**
	 * returns the robot's navigationmodule
	 * @return
	 */
	public NavigationModule getNavigationModule(){
		return this.navCon;
	}
	/**
	 * configures the context of the instruction and executes it
	 * @param ins
	 */
	public void communicateRobot(Instruction ins){	
		if(ins.isUndoAble()) this.lastInstruction = ins;
		ins.configureContext(this, this.navCon, this.getInventory());
		if(this.getFuel() > 0){
			try{
				ins.execute();				
			}catch (InstructionExecutionException e){
				this.emitError(e.getMessage());
			}
		}else{			
			requestQuit();			
		}
	}
	
	/**
	 * gets the last instruction object
	 */
	public Instruction getLastInstruction(){
		return this.lastInstruction;
	}

	/**
	 * Adds an observer to the observer list of the engine
	 * @param o
	 */
	public void addREObserver(RobotEngineObserver o) {
		this.addObserver(o);		
	}
	/**
	 * Adds an observer to the observer list of the navigation module
	 * @param o
	 */
	public void addNObserver(NavigationObserver o) {
		this.getNavigationModule().addObserver(o);		
	}
	/**
	 * Adds an observer to the observer list of the item inventory
	 * @param o
	 */
	public void addIObserver(InventoryObserver o) {
		this.getInventory().addObserver(o);
	}
	/**
	 * Returns the value of the quit attribute
	 * @return
	 */
	public boolean getQuit(){
		return this.quit;
	}
	/**
	 * Informs that the robot arrived at a place
	 */
	public void emitArrivedAtPlace() {
		this.navCon.emitArrivedAtPlace();		
	}
	/**
	 * Raises an error in all the observers
	 * @param string
	 */
	public void emitError(String string) {
		for(RobotEngineObserver reo : this.lista){
			reo.raiseError(string);
		}
		
	}
	/**
	 * Informs that the inventory has changed
	 */
	public void emitInventoryChanged(String message) {		
		for(RobotEngineObserver reo : this.lista){
			if(message != "")
				reo.robotSays(message);			
		}	
		this.items.emitInventoryChanged();
		
	}
	/**
	 * Informs that the game is quitting
	 */
	private void emitQuit(){
		for(RobotEngineObserver reo: this.lista){
			reo.engineOff(this.navCon.atSpaceship());
		}	
	}
	
	public void emitRobotUpdate(){
		for(RobotEngineObserver reo: this.lista){
			reo.robotUpdate(this.fuel, this.recycledMaterial);
		}
	}
	public void emitHeadingChanged() {
		this.navCon.emitHeadingChanged();		
	}
	public void emitUnVisitPlace(Place lastPlace) {
		this.navCon.emitUnVisitPlace(lastPlace);		
	}
	public void emitRoboySays(String string) {
		for(RobotEngineObserver reo : this.lista)
			reo.robotSays(string);
		
	}
}
